<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf8">
    <title>SuperAgent — elegant API for AJAX in Node and browsers</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/3.0.0/tocbot.css">
    <link rel="stylesheet" href="../style.css">
  </head>
  <body>
    <ul id="menu"></ul>
    <div id="content">
<h1 id="superagent">SuperAgent</h1>
<p>SuperAgent是轻量级的渐进式ajax API，具有灵活性、可读性和较低的学习曲线。 它也适用于Node.js!  </p>
<pre><code> request
   .post(&#39;/api/pet&#39;)
   .send({ name: &#39;Manny&#39;, species: &#39;cat&#39; })
   .set(&#39;X-API-Key&#39;, &#39;foobar&#39;)
   .set(&#39;Accept&#39;, &#39;application/json&#39;)
   .then(res =&gt; {
      alert(&#39;yay got &#39; + JSON.stringify(res.body));
   });
</code></pre>
<h2 id="测试文档">测试文档</h2>
<p><a href="/"><strong>English</strong></a></p>
<p>下面的<a href="../test.html">测试文档</a>是用<a href="https://mochajs.org/">Mocha</a>的&quot;文档&quot;报告器生成的，并直接反映了测试套件。 这提供了额外的文档来源。  </p>
<h2 id="基本请求">基本请求</h2>
<p>可以通过调用 <code>request</code> 对象上的适当方法来发起请求，然后调用 <code>.then()</code> ( 或 <code>.end()</code> 或 <a href="#promise-and-generator-support"><code>await</code></a> )发送请求。例如一个简单的 <strong>GET</strong> 请求：</p>
<pre><code> request
   .get(&#39;/search&#39;)
   .then(res =&gt; {
      // res.body, res.headers, res.status
   })
   .catch(err =&gt; {
      // err.message, err.response
   });
</code></pre>
<p>HTTP 方法也可以作为字符串传递：<br>译者注：大小写皆可。</p>
<pre><code>request(&#39;GET&#39;, &#39;/search&#39;).then(success, failure);
</code></pre>
<p>旧式回调也受支持，但不推荐使用。您可以调用 <code>.end()</code> <em>代替</em> <code>.then()</code>：</p>
<pre><code>request(&#39;GET&#39;, &#39;/search&#39;).end(function(err, res){
  if (res.ok) {}
});
</code></pre>
<p>可以使用绝对 URL。在 Web 浏览器中，绝对 URL 仅在服务器实现 <a href="#cors">CORS</a> 时才有效。</p>
<pre><code> request
   .get(&#39;https://example.com/search&#39;)
   .then(res =&gt; {

   });
</code></pre>
<p><strong>Node</strong> 客户端支持向 <a href="https://zh.wikipedia.org/wiki/Unix%E5%9F%9F%E5%A5%97%E6%8E%A5%E5%AD%97">Unix 域套接字</a> 发出请求：</p>
<pre><code>// pattern: https?+unix://SOCKET_PATH/REQUEST_PATH
//在套接字路径中将 `%2F` 用作 `/`
try {
  const res = await request
    .get(&#39;http+unix://%2Fabsolute%2Fpath%2Fto%2Funix.sock/search&#39;);
  // res.body, res.headers, res.status
} catch(err) {
  // err.message, err.response
}
</code></pre>
<p><strong>DELETE__、__HEAD__、__PATCH__、__POST</strong> 和 <strong>PUT</strong> 请求也可以使用，只需更改方法名称：</p>
<pre><code>request
  .head(&#39;/favicon.ico&#39;)
  .then(res =&gt; {

  });
</code></pre>
<p><strong>DELETE</strong> 也可以用 <code>.del()</code> 调用以与旧版 IE 兼容，其中 <code>delete</code> 是保留字。</p>
<p>HTTP 方法默认为 __GET__，因此如果您愿意，以下代码是有效的：</p>
<pre><code> request(&#39;/search&#39;, (err, res) =&gt; {

 });
</code></pre>
<h2 id="使用-http-2">使用 HTTP/2</h2>
<p>要使用 HTTP/2 协议（没有 HTTP/1.x 后备），请使用 <code>.http2()</code> 方法。</p>
<pre><code class="language-javascript">    const request = require(&#39;superagent&#39;);
    const res = await request
      .get(&#39;https://example.com/h2&#39;)
      .http2();
</code></pre>
<h2 id="设置请求头字段">设置请求头字段</h2>
<p>设置请求头字段很简单，调用 <code>.set()</code> 时传入字段名称和值：</p>
<pre><code> request
   .get(&#39;/search&#39;)
   .set(&#39;API-Key&#39;, &#39;foobar&#39;)
   .set(&#39;Accept&#39;, &#39;application/json&#39;)
   .then(callback);
</code></pre>
<p>您还可以在一次调用中传入一个对象来设置多个字段：</p>
<pre><code> request
   .get(&#39;/search&#39;)
   .set({ &#39;API-Key&#39;: &#39;foobar&#39;, Accept: &#39;application/json&#39; })
   .then(callback);
</code></pre>
<h2 id="get-请求"><code>GET</code> 请求</h2>
<p><code>.query()</code> 方法接受对象，当与 <strong>GET</strong> 方法一起使用时将形成一个查询字符串。以下将产生路径 <code>/search?query=Manny&amp;range=1..5&amp;order=desc</code>。
译者注：<code>.query()</code> 方法的参数不需要提前进行url编码。</p>
<pre><code> request
   .get(&#39;/search&#39;)
   .query({ query: &#39;Manny&#39; })
   .query({ range: &#39;1..5&#39; })
   .query({ order: &#39;desc&#39; })
   .then(res =&gt; {

   });
</code></pre>
<p>或传入单个对象：</p>
<pre><code>request
  .get(&#39;/search&#39;)
  .query({ query: &#39;Manny&#39;, range: &#39;1..5&#39;, order: &#39;desc&#39; })
  .then(res =&gt; {

  });
</code></pre>
<p><code>.query()</code> 方法也可以接受字符串。</p>
<pre><code>  request
    .get(&#39;/querystring&#39;)
    .query(&#39;search=Manny&amp;range=1..5&#39;)
    .then(res =&gt; {

    });
</code></pre>
<p>或者一个个加入：</p>
<pre><code>  request
    .get(&#39;/querystring&#39;)
    .query(&#39;search=Manny&#39;)
    .query(&#39;range=1..5&#39;)
    .then(res =&gt; {

    });
</code></pre>
<h2 id="head-请求"><code>HEAD</code> 请求</h2>
<p>您还可以对 <strong>HEAD</strong> 请求使用 .query() 方法。以下将生成路径 <code>/users?email=joe@smith.com</code>。</p>
<pre><code>  request
    .head(&#39;/users&#39;)
    .query({ email: &#39;joe@smith.com&#39; })
    .then(res =&gt; {

    });
</code></pre>
<h2 id="post--put-请求"><code>POST</code> / <code>PUT</code> 请求</h2>
<p>一个典型的 JSON <strong>POST</strong> 请求可能如下所示，我们适当地设置 <code>Content-Type</code> 请求头字段，并&quot;写入&quot;一些数据，在本例中只是一个 JSON 字符串。</p>
<pre><code>  request.post(&#39;/user&#39;)
    .set(&#39;Content-Type&#39;, &#39;application/json&#39;)
    .send(&#39;{&quot;name&quot;:&quot;tj&quot;,&quot;pet&quot;:&quot;tobi&quot;}&#39;)
    .then(callback)
    .catch(errorCallback)
</code></pre>
<p>由于 JSON 无疑是最常见的，所以它是 <em>默认</em> 的！下面的例子与前面的例子是等价的。</p>
<pre><code>  request.post(&#39;/user&#39;)
    .send({ name: &#39;tj&#39;, pet: &#39;tobi&#39; })
    .then(callback, errorCallback)
</code></pre>
<p>或者调用多个 <code>.send()</code>：</p>
<pre><code>  request.post(&#39;/user&#39;)
    .send({ name: &#39;tj&#39; })
    .send({ pet: &#39;tobi&#39; })
    .then(callback, errorCallback)
</code></pre>
<p>默认情况下，发送字符串会将 <code>Content-Type</code> 设置为 <code>application/x-www-form-urlencoded</code>，多个调用将用 <code>&amp;</code> 连接，这里产生 <code>name=tj&amp;pet=tobi</code>：</p>
<pre><code>  request.post(&#39;/user&#39;)
    .send(&#39;name=tj&#39;)
    .send(&#39;pet=tobi&#39;)
    .then(callback, errorCallback);
</code></pre>
<p>SuperAgent 格式是可扩展的，但默认情况下支持 &quot;json&quot; 和 &quot;form&quot;。要将数据作为 <code>application/x-www-form-urlencoded</code> 发送，只需在调用 <code>.type()</code> 时传入 &quot;form&quot;，默认为 &quot;json&quot;。此 <strong>POST</strong> 请求的请求体将是 &quot;name=tj&amp;pet=tobi&quot;。</p>
<pre><code>  request.post(&#39;/user&#39;)
    .type(&#39;form&#39;)
    .send({ name: &#39;tj&#39; })
    .send({ pet: &#39;tobi&#39; })
    .then(callback, errorCallback)
</code></pre>
<p>还支持发送 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/FormData"><code>FormData</code></a> 对象。以下示例将 <strong>POST</strong> 请求由 id=&quot;myForm&quot; 标识的 HTML 表单的内容：</p>
<pre><code>  request.post(&#39;/user&#39;)
    .send(new FormData(document.getElementById(&#39;myForm&#39;)))
    .then(callback, errorCallback)
</code></pre>
<h2 id="设置-content-type">设置 <code>Content-Type</code></h2>
<p>显而易见的解决方案是使用 <code>.set()</code> 方法：</p>
<pre><code> request.post(&#39;/user&#39;)
   .set(&#39;Content-Type&#39;, &#39;application/json&#39;)
</code></pre>
<p><code>.type()</code> 方法也可以作为简写，接受带有类型/子类型的规范化 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types">MIME 类型</a> 名称，或者只是扩展名，例如&quot;xml&quot;、&quot;json&quot;、&quot;png&quot;等：</p>
<pre><code> request.post(&#39;/user&#39;)
   .type(&#39;application/json&#39;)

 request.post(&#39;/user&#39;)
   .type(&#39;json&#39;)

 request.post(&#39;/user&#39;)
   .type(&#39;png&#39;)
</code></pre>
<h2 id="序列化请求体">序列化请求体</h2>
<p>SuperAgent 将自动序列化 JSON 和表单。您也可以为其他类型设置自动序列化：</p>
<pre><code class="language-js">request.serialize[&#39;application/xml&#39;] = function (obj) {
    return &#39;从obj生成的字符串&#39;;
};

// 接下来，内容类型为 &quot;application/xml&quot; 的所有请求都将自动序列化
</code></pre>
<p>如果您想以自定义格式发送 数据体(payload)，您可以根据每个请求将内置序列化替换为 <code>.serialize()</code> 方法：</p>
<pre><code class="language-js">request
    .post(&#39;/user&#39;)
    .send({foo: &#39;bar&#39;})
    .serialize(obj =&gt; {
        return &#39;从obj生成的字符串&#39;;
    });
</code></pre>
<h2 id="重试请求">重试请求</h2>
<p>如果请求暂时失败或可能是网络连接不稳定造成的失败，且当给定 <code>.retry()</code> 方法时，SuperAgent 将自动重试请求。</p>
<p>此方法有两个可选参数：重试次数（默认为 <code>1</code>）和回调函数。它在每次重试之前调用 callback(err, res) 。回调可以返回 <code>true</code>/<code>false</code> 以控制是否应重试请求（但始终应该用最大重试次数）。
     request
       .get(&#39;<a href="https://example.com/search&#39;">https://example.com/search&#39;</a>)
       .retry(2) // 或者：
       .retry(2, callback) // 二选一
       .then(finished);
       .catch(failed);</p>
<p><code>.retry()</code> 仅用于<a href="https://baike.baidu.com/item/%E5%B9%82%E7%AD%89/8600688?fr=aladdin"><em>幂等</em></a>请求（即到达服务器的多个请求不会导致重复购买等不良副作用）。</p>
<p>默认情况下会尝试所有请求方法（这意味着如果您不希望重试 POST 请求，则需要传递自定义的重试回调函数）。</p>
<p>默认情况下会重试以下状态代码：</p>
<ul>
<li><code>408</code></li>
<li><code>413</code></li>
<li><code>429</code></li>
<li><code>500</code></li>
<li><code>502</code></li>
<li><code>503</code></li>
<li><code>504</code></li>
<li><code>521</code></li>
<li><code>522</code></li>
<li><code>524</code></li>
</ul>
<p>默认情况下会重试以下错误代码：</p>
<ul>
<li><code>&#39;ETIMEDOUT&#39;</code></li>
<li><code>&#39;ECONNRESET&#39;</code></li>
<li><code>&#39;EADDRINUSE&#39;</code></li>
<li><code>&#39;ECONNREFUSED&#39;</code></li>
<li><code>&#39;EPIPE&#39;</code></li>
<li><code>&#39;ENOTFOUND&#39;</code></li>
<li><code>&#39;ENETUNREACH&#39;</code></li>
<li><code>&#39;EAI_AGAIN&#39;</code></li>
</ul>
<h2 id="设置-accept">设置 Accept</h2>
<p>与 <code>.type()</code> 方法类似，也可以通过简写方法 <code>.accept()</code> 设置 <code>Accept</code> 请求头。方便起见，其中还引用了 <code>request.types</code>，允许您将完整的规范化 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types">MIME 类型</a> 名称指定为<code>类型/子类型</code>，或将扩展后缀形式指定为&quot;xml&quot;、&quot;json&quot;、&quot;png&quot;等：</p>
<pre><code> request.get(&#39;/user&#39;)
   .accept(&#39;application/json&#39;)

 request.get(&#39;/user&#39;)
   .accept(&#39;json&#39;)

 request.post(&#39;/user&#39;)
   .accept(&#39;png&#39;)
</code></pre>
<h3 id="facebook-和-accept-json">Facebook 和 Accept JSON</h3>
<p>如果您正在调用 Facebook 的 API，请务必在您的请求中发送 <code>Accept: application/json</code> 请求头。如果你不这样做，Facebook 会回复 <code>Content-Type: text/javascript; charset=UTF-8</code>，SuperAgent 将不会解析，因此 <code>res.body</code> 将是未定义的。您可以使用 <code>req.accept(&#39;json&#39;)</code> 或 <code>req.header(&#39;Accept&#39;, &#39;application/json&#39;)</code> 来执行此操作。有关详细信息，请参阅 <a href="https://github.com/ladjs/superagent/issues/1078">issue 1078</a>。</p>
<h2 id="查询字符串query-strings">查询字符串(Query strings)</h2>
<p><code>req.query(obj)</code> 是一种可用于构建查询字符串的方法。例如在 <strong>POST</strong> 上增加 <code>?format=json&amp;dest=/login</code>：</p>
<pre><code>request
  .post(&#39;/&#39;)
  .query({ format: &#39;json&#39; })
  .query({ dest: &#39;/login&#39; })
  .send({ post: &#39;data&#39;, here: &#39;wahoo&#39; })
  .then(callback);
</code></pre>
<p>默认情况下，查询字符串不按任何特定顺序组装。可以使用 <code>req.sortQuery()</code> 启用 ASCIIbetically 排序的查询字符串。您还可以使用 <code>req.sortQuery(myComparisonFn)</code> 提供自定义排序比较函数。比较函数应该接受 2 个参数并返回一个负/零/正整数。</p>
<pre><code class="language-js"> // 默认顺序
 request.get(&#39;/user&#39;)
   .query(&#39;name=Nick&#39;)
   .query(&#39;search=Manny&#39;)
   .sortQuery()
   .then(callback)

 // 自定义排序函数
 request.get(&#39;/user&#39;)
   .query(&#39;name=Nick&#39;)
   .query(&#39;search=Manny&#39;)
   .sortQuery((a, b) =&gt; a.length - b.length)
   .then(callback)
</code></pre>
<h2 id="tls-选项">TLS 选项</h2>
<p>在 Node.js 中，SuperAgent 支持配置 HTTPS 请求的方法：</p>
<ul>
<li><code>.ca()</code>: 将 CA 证书设置为信任</li>
<li><code>.cert()</code>: 设置客户端证书链</li>
<li><code>.key()</code>: 设置客户端私钥</li>
<li><code>.pfx()</code>: 设置客户端 PFX 或 PKCS12 编码的私钥和证书链</li>
<li><code>.disableTLSCerts()</code>: 不拒绝过期或无效的 TLS 证书。在内部设置 <code>rejectUnauthorized=true</code>。<em>请注意，此方法允许中间人攻击。</em></li>
</ul>
<p>有关更多信息，请参阅 Node.js <a href="http://nodejs.cn/api/https.html#httpsrequesturl-options-callback">https.request 文档</a>。</p>
<pre><code class="language-js">var key = fs.readFileSync(&#39;key.pem&#39;),
    cert = fs.readFileSync(&#39;cert.pem&#39;);

request
  .post(&#39;/client-auth&#39;)
  .key(key)
  .cert(cert)
  .then(callback);
</code></pre>
<pre><code class="language-js">var ca = fs.readFileSync(&#39;ca.cert.pem&#39;);

request
  .post(&#39;https://localhost/private-ca-server&#39;)
  .ca(ca)
  .then(res =&gt; {});
</code></pre>
<h2 id="解析响应体">解析响应体</h2>
<p>SuperAgent将为您解析已知的响应主体数据，目前支持<code>application/x-www-form-urlencoded</code>，<code>application/json</code>，以及<code>multipart/form data</code>。您可以设置自动解析其他响应主体数据：</p>
<pre><code class="language-js">//浏览器
request.parse[&#39;application/xml&#39;] = function (str) {
    return {&#39;object&#39;: &#39;从str解析的&#39;};
};

//node
request.parse[&#39;application/xml&#39;] = function (res, cb) {
    //解析响应文本并在此处设置res.body

    cb(null, res);
};

//接下来，将自动解析 &#39;application/xml&#39; 类型的响应
</code></pre>
<p>您可以使用 <code>.buffer(true).parse(fn)</code> 方法设置自定义解析器（优先于内置解析器）。如果未启用响应缓冲 (<code>.buffer(false)</code>)，则将触发<code>响应(response)</code>事件而无需等待正文解析器完成，因此 <code>response.body</code> 将不可用。</p>
<h3 id="json--urlencoded">JSON / Urlencoded</h3>
<p>属性 <code>res.body</code> 是解析后的对象，例如，如果请求以 JSON 字符串 &#39;{&quot;user&quot;:{&quot;name&quot;:&quot;tobi&quot;}}&#39; 响应，则 <code>res.body.user.name</code> 将为 &quot;tobi&quot; .同样，&quot;user[name]=tobi&quot; 的 x-www-form-urlencoded 值将产生相同的结果。仅支持一级嵌套。如果您需要更复杂的数据，请改为发送 JSON。</p>
<p>通过重复的键发送数组。 <code>.send({color: [&#39;red&#39;,&#39;blue&#39;]})</code> 会发送 <code>color=red&amp;color=blue</code>。如果您希望数组键的名称中包含 <code>[]</code>，您必须自己添加它，因为 SuperAgent 不会自动添加它。</p>
<h3 id="multipart">Multipart</h3>
<p>Node 客户端通过 <a href="https://github.com/felixge/node-formidable">Formidable</a> 模块支持 _multipart/form-data_。解析 multipart 响应时，对象 <code>res.files</code> 也可供您使用。例如，假设一个请求使用以下 multipart 请求体进行响应：</p>
<pre><code>--whoop
Content-Disposition: attachment; name=&quot;image&quot;; filename=&quot;tobi.png&quot;
Content-Type: image/png

... data here ...
--whoop
Content-Disposition: form-data; name=&quot;name&quot;
Content-Type: text/plain

Tobi
--whoop--
</code></pre>
<p><code>res.body.name</code>的值将为 &quot;Tobi&quot;，并且 <code>res.files.image</code> 将作为包含磁盘路径、文件名和其他属性的 <code>File</code> 对象。</p>
<h3 id="二进制数据">二进制数据</h3>
<p>在浏览器中，您可以使用 <code>.responseType(&#39;blob&#39;)</code> 来请求处理二进制响应体。在 node.js 中运行时不需要此 API。此方法支持的参数值为</p>
<ul>
<li><code>&#39;blob&#39;</code> 赋值给 XmlHTTPRequest 的 <code>responseType</code> 属性</li>
<li><code>&#39;arraybuffer&#39;</code> 赋值给 XmlHTTPRequest 的 responseType 属性</li>
</ul>
<pre><code class="language-js">req.get(&#39;/binary.data&#39;)
  .responseType(&#39;blob&#39;)
  .then(res =&gt; {
    // res.body 将是浏览器原生 Blob 类型
  });
</code></pre>
<p>有关更多信息，请参阅 Mozilla 开发人员网络 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/responseType">xhr.responseType 文档</a>。</p>
<h2 id="响应属性">响应属性</h2>
<p>在 <code>Response</code> 对象上设置了许多有用的标志和属性，包括响应文本、解析的响应正文、响应头字段、状态标志等等。</p>
<h3 id="响应文本">响应文本</h3>
<p><code>res.text</code> 属性包含未解析的响应正文字符串。此属性始终存在于客户端 API 中，并且仅当默认情况下节点的 mime 类型与 &quot;text/<em>&quot;、&quot;</em>/json&quot; 或 &quot;x-www-form-urlencoded&quot; 匹配时。原因是为了节省内存，因为缓冲大型正文（例如 multipart 文件或图像）的文本效率极低。要强制缓冲，请参阅&quot;<a href="#%E7%BC%93%E5%86%B2%E5%93%8D%E5%BA%94">缓冲响应</a>&quot;部分。</p>
<h3 id="响应体">响应体</h3>
<p>就像 SuperAgent 可以自动序列化请求数据一样，它也可以自动解析响应体。为 Content-Type 定义解析器时，会对其进行解析，默认情况下包括 &quot;application/json&quot; 和 &quot;application/x-www-form-urlencoded&quot;。然后可以通过 <code>res.body</code> 获得解析的对象。</p>
<h3 id="响应头字段">响应头字段</h3>
<p><code>res.header</code> 包含已解析的响应头字段的对象，字段名称小写，就像 node 做的一样。例如 <code>res.header[&#39;content-length&#39;]</code>。</p>
<h3 id="响应内容类型content-type">响应内容类型(Content-Type)</h3>
<p>Content-Type 响应头是特殊情况，提供 <code>res.type</code>，它没有字符集（也可以有）。例如，&quot;text/html; charset=utf8&quot; 的 <code>Content-Type</code> 将提供 &quot;text/html&quot; 作为 <code>res.type</code>，然后 <code>res.charset</code> 属性将包含 &quot;utf8&quot;。</p>
<h3 id="响应状态">响应状态</h3>
<p>响应状态标志有助于确定请求是否成功，以及其他有用的信息，使 SuperAgent 成为与 RESTful Web 服务交互的理想选择。这些标志当前定义为：</p>
<pre><code> var type = status / 100 | 0;

 // status / class
 res.status = status;
 res.statusType = type;

 // basics
 res.info = 1 == type;
 res.ok = 2 == type;
 res.clientError = 4 == type;
 res.serverError = 5 == type;
 res.error = 4 == type || 5 == type;

 // 语法糖
 res.accepted = 202 == status;
 res.noContent = 204 == status || 1223 == status;
 res.badRequest = 400 == status;
 res.unauthorized = 401 == status;
 res.notAcceptable = 406 == status;
 res.notFound = 404 == status;
 res.forbidden = 403 == status;
</code></pre>
<h2 id="中止请求">中止请求</h2>
<p>要中止请求，只需调用 <code>req.abort()</code> 方法。</p>
<h2 id="超时设定">超时设定</h2>
<p>有时网络和服务器会 &quot;卡住&quot; 并且在接受请求后从不响应。设置超时以避免请求永远等待。</p>
<ul>
<li><p><code>req.timeout({deadline:ms})</code> 或 <code>req.timeout(ms)</code>（其中 <code>ms</code> 是毫秒数 &gt; 0）设置完成整个请求（包括所有上传、重定向、服务器处理时间）的最后期限。如果在这段时间内没有完全下载响应，则请求将被中止。</p>
</li>
<li><p><code>req.timeout({response:ms})</code> 设置等待第一个字节从服务器到达的最长时间，但它不限制整个下载需要多长时间。响应超时应该至少比服务器响应的时间长几秒钟，因为它还包括进行 DNS 查找、TCP/IP 和 TLS 连接的时间，以及上传请求数据的时间。</p>
</li>
</ul>
<p>您应该同时使用 <code>deadline</code> 和 <code>response</code> 超时。通过这种方式，您可以使用较短的响应超时来快速检测无响应的网络，并使用较长的截止时间来为缓慢但可靠的网络上的下载留出时间。请注意，这两个计时器都限制了允许<em>上传</em>附件的时间。如果您要上传文件，请使用长超时。</p>
<pre><code>request
  .get(&#39;/big-file?network=slow&#39;)
  .timeout({
    response: 5000,  // 等待 5 秒让服务器开始发送
    deadline: 60000, // 但允许文件用 1 分钟完成加载。
  })
  .then(res =&gt; {
      /* 及时响应 */
    }, err =&gt; {
      if (err.timeout) { /* 超时! */ } else { /* 其他错误 */ }
  });
</code></pre>
<p>超时错误有个 <code>.timeout</code> 属性。</p>
<h2 id="验证">验证</h2>
<p>在 Node 和浏览器中都可以通过 <code>.auth()</code> 方法进行身份验证：</p>
<pre><code>request
  .get(&#39;http://local&#39;)
  .auth(&#39;tobi&#39;, &#39;learnboost&#39;)
  .then(callback);
</code></pre>
<p>在 <em>Node</em> 客户端中，基本身份验证可以在 URL 中写成 &quot;user:pass&quot;：</p>
<pre><code>request.get(&#39;http://tobi:learnboost@local&#39;).then(callback);
</code></pre>
<p>默认情况下，仅使用<code>基本(Basic)</code>身份验证。在浏览器中，您可以添加 <code>{type:&#39;auto&#39;}</code> 以启用浏览器中内置的所有方法（Digest、NTLM 等）：</p>
<pre><code>request.auth(&#39;digest&#39;, &#39;secret&#39;, {type:&#39;auto&#39;})
</code></pre>
<p><code>auth</code> 方法还支持一种<code>承载类型</code>，以指定基于令牌的身份验证：</p>
<pre><code>request.auth(&#39;my_token&#39;, { type: &#39;bearer&#39; })
</code></pre>
<h2 id="跟随重定向">跟随重定向</h2>
<p>默认情况下将跟随最多 5 个重定向，但是您可以使用 <code>res.redirects(n)</code> 方法指定它：</p>
<pre><code>const response = await request.get(&#39;/some.png&#39;).redirects(2);
</code></pre>
<p>超出限制的重定向被视为错误。使用 <code>.ok(res =&gt; res.status &lt; 400)</code> 将它们读取为成功响应。</p>
<h2 id="全局状态代理程序">全局状态代理程序</h2>
<h3 id="保存-cookie">保存 cookie</h3>
<p>在 Node 中 SuperAgent 默认不保存 cookie，但您可以使用 <code>.agent()</code> 方法创建保存 cookie 的 SuperAgent 副本。每个副本都有一个单独的 cookie 储存器。</p>
<pre><code>const agent = request.agent();
agent
  .post(&#39;/login&#39;)
  .then(() =&gt; {
    return agent.get(&#39;/cookied-page&#39;);
  });
</code></pre>
<p>在浏览器中，cookie 由浏览器自动管理，因此 <code>.agent()</code> 不会隔离 cookie。</p>
<h3 id="多个请求的默认选项">多个请求的默认选项</h3>
<p>代理程序上调用的常规请求方法将用作该代理发出的所有请求的默认值。</p>
<pre><code>const agent = request.agent()
  .use(plugin)
  .auth(shared);

await agent.get(&#39;/with-plugin-and-auth&#39;); // 带有插件和身份验证
await agent.get(&#39;/also-with-plugin-and-auth&#39;); // 也带有插件和身份验证
</code></pre>
<p>代理可以用来设置默认值的完整方法列表是：<code>use</code>、 <code>on</code>、 <code>once</code>、 <code>set</code>、 <code>query</code>、 <code>type</code>、 <code>accept</code>、 <code>auth</code>、 <code>withCredentials</code>、 <code>sortQuery</code>、 <code>retry</code>、 <code>ok</code>、 <code>redirects</code>、 <code>timeout</code>、 <code>buffer</code>、 <code>serialize</code>、 <code>parse</code>、 <code>ca</code>、 <code>key</code>、 <code>pfx</code>、 <code>cert</code>.</p>
<h2 id="管道数据">管道数据</h2>
<p>Node 客户端允许您通过管道将数据传入和传出请求。请注意，使用 <code>.pipe()</code> <strong>代替</strong> <code>.end()/.then()</code> 方法。</p>
<p>管道文件的内容作为请求的例子：</p>
<pre><code>const request = require(&#39;superagent&#39;);
const fs = require(&#39;fs&#39;);

const stream = fs.createReadStream(&#39;path/to/my.json&#39;);
const req = request.post(&#39;/somewhere&#39;);
req.type(&#39;json&#39;);
stream.pipe(req);
</code></pre>
<p>请注意，当您通过管道发送请求时，superagent 使用<a href="https://baike.baidu.com/item/%E5%88%86%E5%9D%97%E4%BC%A0%E8%BE%93%E7%BC%96%E7%A0%81/8359216?fr=aladdin">分块传输编码</a>发送管道数据，并非所有服务器（例如 Python WSGI 服务器）都支持。</p>
<p>或将响应传送到文件：</p>
<pre><code>const stream = fs.createWriteStream(&#39;path/to/my.json&#39;);
const req = request.get(&#39;/some.json&#39;);
req.pipe(stream);
</code></pre>
<p> 不能混合使用管道和回调函数或 promises。请注意，您<strong>不应</strong>尝试通过管道传输 <code>.end()</code> 或 <code>Response</code> 对象的结果：</p>
<pre><code>// 别特么这么写：
const stream = getAWritableStream();
const req = request
  .get(&#39;/some.json&#39;)
  // BAD: 这会将无用信息管道传输到流中并以意想不到的方式失败
  .end((err, this_does_not_work) =&gt; this_does_not_work.pipe(stream))
const req = request
  .get(&#39;/some.json&#39;)
  .end()
  // BAD: 这也不支持，调用 .end 之后调用 .pipe。
  .pipe(nope_its_too_late);
</code></pre>
<p>在 superagent 的<a href="https://github.com/ladjs/superagent/issues/1188">未来版本</a>中，对 <code>pipe()</code> 的不当调用将失败。</p>
<h2 id="多部分请求">多部分请求</h2>
<p>SuperAgent 也非常适合 <em>构建</em> 它提供方法 <code>.attach()</code> 和 <code>.field()</code> 的多部分请求。</p>
<p>当您使用 <code>.field()</code> 或 <code>.attach()</code> 时，您不能使用 <code>.send()</code> 并且您<em>不能</em>设置 <code>Content-Type</code>（将为您设置正确的类型）。</p>
<h3 id="附加文件">附加文件</h3>
<p>要发送文件，请使用 <code>.attach(name, [file], [options])</code>。您可以通过多次调用 <code>.attach</code> 来附加多个文件。参数是：</p>
<ul>
<li><code>name</code> — form 表单中的字段名。</li>
<li><code>file</code> — 带有文件路径的字符串或 <code>Blob/Buffer</code> 对象。</li>
<li><code>options</code> — （可选）自定义文件名的字符串或 <code>{filename: string}</code> 对象。在 Node 中也支持 <code>{contentType: &#39;mime/type&#39;}</code>。在浏览器中创建一个具有适当类型的 <code>Blob</code>。</li>
</ul>
<br>

<pre><code>request
  .post(&#39;/upload&#39;)
  .attach(&#39;image1&#39;, &#39;path/to/felix.jpeg&#39;)
  .attach(&#39;image2&#39;, imageBuffer, &#39;luna.jpeg&#39;)
  .field(&#39;caption&#39;, &#39;My cats&#39;)
  .then(callback);
</code></pre>
<h3 id="字段值">字段值</h3>
<p>与 HTML 中的表单字段非常相似，您可以使用 <code>.field(name, value)</code> 和 <code>.field({name: value})</code> 设置字段值。假设您想上传一些带有您的姓名和电子邮件的图片，您的请求可能如下所示：</p>
<pre><code> request
   .post(&#39;/upload&#39;)
   .field(&#39;user[name]&#39;, &#39;Tobi&#39;)
   .field(&#39;user[email]&#39;, &#39;tobi@learnboost.com&#39;)
   .field(&#39;friends[]&#39;, [&#39;loki&#39;, &#39;jane&#39;])
   .attach(&#39;image&#39;, &#39;path/to/tobi.png&#39;)
   .then(callback);
</code></pre>
<h2 id="压缩">压缩</h2>
<p>node 客户端支持压缩过的响应，最重要的是，您无需执行任何操作！它就能用。</p>
<h2 id="缓冲响应">缓冲响应</h2>
<p>要强制将响应主体缓冲为 <code>res.text</code>，您可以调用 <code>req.buffer()</code>。要取消对文本响应（例如 &quot;text/plain&quot;、&quot;text/html&quot; 等）的默认缓冲，您可以调用 <code>req.buffer(false)</code>。 </p>
<p>当缓冲提供 <code>res.buffered</code> 标志时，您可以使用它在同一个回调中处理缓冲和非缓冲响应。</p>
<h2 id="cors">CORS</h2>
<p>出于安全原因，浏览器将阻止跨域请求，除非服务器选择使用 CORS 标头。浏览器还会发出额外的 <strong>OPTIONS</strong> 请求来检查服务器允许哪些 HTTP 标头和方法。<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS">阅读有关 CORS 的更多信息</a>。</p>
<p><code>.withCredentials()</code> 方法支持从源发送 cookie，但仅当 Access-Control-Allow-Origin <em>不是</em> 通配符 (&quot;*&quot;) 且 <code>Access-Control-Allow-Credentials</code> 为 &quot;true&quot; 时。</p>
<pre><code>request
  .get(&#39;https://api.example.com:4001/&#39;)
  .withCredentials()
  .then(res =&gt; {
    assert.equal(200, res.status);
    assert.equal(&#39;tobi&#39;, res.text);
  })
</code></pre>
<h2 id="错误处理">错误处理</h2>
<p>您的回调函数将始终传递两个参数：错误和响应。如果没有发生错误，第一个参数将为<code>null</code>：</p>
<pre><code>request
 .post(&#39;/upload&#39;)
 .attach(&#39;image&#39;, &#39;path/to/tobi.png&#39;)
 .then(res =&gt; {

 });
</code></pre>
<p>还会触发&quot;错误&quot;事件，您可以监听：</p>
<pre><code>request
  .post(&#39;/upload&#39;)
  .attach(&#39;image&#39;, &#39;path/to/tobi.png&#39;)
  .on(&#39;error&#39;, handle)
  .then(res =&gt; {

  });
</code></pre>
<p>请注意，<strong>默认情况下，superagent 会考虑 4xx 和 5xx 响应（以及未处理的 3xx 响应）视为错误</strong>。例如，如果您收到 <code>304 Not modified</code>、<code>403 Forbidden</code> 或 <code>500 Internal server</code> 错误响应，则此状态信息将通过 <code>err.status</code> 提供。来自此类响应的错误还包含一个 <code>err.response</code> 字段，其中包含&quot;<a href="#%E5%93%8D%E5%BA%94%E5%B1%9E%E6%80%A7">响应属性</a>&quot;中提到的所有属性。该库以这种方式运行以处理需要成功响应并将 HTTP 错误状态代码视为错误的常见情况，同时仍允许围绕特定错误条件进行自定义逻辑。</p>
<p>网络故障、超时和其他不产生响应的错误将不包含 <code>err.status</code> 或 <code>err.response</code> 字段。</p>
<p>如果您希望处理 404 或其他 HTTP 错误响应，您可以查询 <code>err.status</code> 属性。当发生 HTTP 错误（4xx 或 5xx 响应）时， <code>res.error</code> 属性是一个 <code>Error</code> 对象，这允许您执行以下检查：</p>
<pre><code>if (err &amp;&amp; err.status === 404) {
  alert(&#39;oh no &#39; + res.body.message);
}
else if (err) {
  // 所有其他需要处理的错误类型
}
</code></pre>
<p>或者，您可以使用 <code>.ok(callback)</code> 方法来确定响应是否为错误。 <code>ok</code> 函数的回调函数获得响应，如果响应应该被解释为成功，则返回 true。</p>
<pre><code>request.get(&#39;/404&#39;)
  .ok(res =&gt; res.status &lt; 500)
  .then(response =&gt; {
    // 将 404 页面作为成功响应
  })
</code></pre>
<h2 id="进度跟踪">进度跟踪</h2>
<p>SuperAgent 在上传和下载大文件时触发 <code>progress</code> 事件。</p>
<pre><code>request.post(url)
  .attach(&#39;field_name&#39;, file)
  .on(&#39;progress&#39;, event =&gt; {
    /* event的值：
    {
      direction: &quot;upload&quot; or &quot;download&quot;
      percent: 0 to 100 // 如果文件大小未知，可能会没有
      total: // 总文件大小，可能没有
      loaded: // 到目前为止下载或上传的字节数
    } */
  })
  .then()
</code></pre>
<h2 id="在本地主机上测试">在本地主机上测试</h2>
<h3 id="强制连接特定-ip-地址">强制连接特定 IP 地址</h3>
<p>在 Node.js 中，可以忽略 DNS 解析并使用 <code>.connect()</code> 方法将所有请求定向到特定 IP 地址。例如，此请求将转到 localhost 而不是 <code>example.com</code>：</p>
<pre><code>const res = await request.get(&quot;http://example.com&quot;).connect(&quot;127.0.0.1&quot;);
</code></pre>
<p>因为请求可能被重定向，所以可以指定多个主机名和多个 IP，以及一个特殊的 <code>*</code> 作为后备（注意：不支持其他通配符）。请求将保留其 <code>Host</code> 请求头的原始值。</p>
<pre><code>const res = await request.get(&quot;http://redir.example.com:555&quot;)
  .connect({
    &quot;redir.example.com&quot;: &quot;127.0.0.1&quot;, // redir.example.com:555 将使用 127.0.0.1:555
    &quot;www.example.com&quot;: false, // 不覆盖这个；正常使用 DNS
    &quot;mapped.example.com&quot;: { host: &quot;127.0.0.1&quot;, port: 8080}, // mapped.example.com:* 将使用 127.0.0.1:8080
    &quot;*&quot;: &quot;proxy.example.com&quot;, // 所有其他请求都将发送到该主机
  });
</code></pre>
<h3 id="忽略本地主机上损坏不安全的-https">忽略本地主机上损坏/不安全的 HTTPS</h3>
<p>在 Node.js 中，当 HTTPS 配置错误且不安全（例如，使用自签名证书而<em>不指定</em>自己的 <code>.ca()</code>）时，仍然可以通过调用 <code>.trustLocalhost()</code> 来允许对 <code>localhost</code> 的请求：</p>
<pre><code>const res = await request.get(&quot;https://localhost&quot;).trustLocalhost()
</code></pre>
<p>与 <code>.connect(&quot;127.0.0.1&quot;)</code> 一起，这可用于强制将对任何域的 HTTPS 请求重新路由到 <code>localhost</code>。</p>
<p>忽略本地主机上损坏的 HTTPS 通常是安全的，因为环回接口不会暴露给不受信任的网络。信任 <code>localhost</code> 可能会成为未来的默认设置。使用 <code>.trustLocalhost(false)</code> 强制检查 <code>127.0.0.1</code> 的可靠性。</p>
<p>当向任何其他 IP 发出请求时，我们故意不支持禁用 HTTPS 安全性，因为这些选项最终被滥用为 HTTPS 问题的快速&quot;修复&quot;。您可以从 <a href="https://certbot.eff.org">Let&#39;s Encrypt</a> 获得免费的 HTTPS 证书或设置您自己的 CA (<code>.ca(ca_public_pem)</code>) 以使您的自签名证书受信任。</p>
<h2 id="promise-和生成器函数支持">Promise 和生成器函数支持</h2>
<p>SuperAgent 的请求是一个 &quot;thenable&quot; 对象(带有then方法的对象)，它与 JavaScript Promise 和 <code>async/await</code> 语法兼容。</p>
<pre><code>const res = await request.get(url);
</code></pre>
<p>如果你使用 Promise，<strong>不要</strong>调用 <code>.end()</code> 或 <code>.pipe()</code>。任何使用 <code>.then()</code> 或 <code>await</code> 都会禁用所有其他使用请求的方式。 像 <a href="https://github.com/tj/co">co</a> 这样的库或像 <a href="https://github.com/koajs/koa">koa</a> 这样的 web 框架可以在任何 SuperAgent 方法上 <code>yield</code>：</p>
<pre><code>const req = request
  .get(&#39;http://local&#39;)
  .auth(&#39;tobi&#39;, &#39;learnboost&#39;);
const res = yield req;
</code></pre>
<p>请注意，SuperAgent 期望全局 <code>Promise</code> 对象存在。您需要一个 polyfill 才能在 Internet Explorer 或 Node.js 0.10 中使用 Promise。</p>
<h2 id="浏览器和-node-版本">浏览器和 node 版本</h2>
<p>SuperAgent 有两种实现：一种用于 Web 浏览器（使用 XHR），另一种用于 Node.JS（使用核心 http 模块）。默认情况下，Browserify 和 WebPack 将选择浏览器版本。 </p>
<p>如果要使用 WebPack 为 Node.JS 编译代码，您<em>必须</em>在其配置中指定<a href="https://webpack.github.io/docs/configuration.html#target">node target</a>。</p>
<h3 id="在-electron-中使用浏览器版本">在 electron 中使用浏览器版本</h3>
<p><a href="https://electron.atom.io/">Electron</a> 开发人员报告说，如果您希望使用浏览器版本的 SuperAgent 而不是 Node 版本，您可以 <code>require(&#39;superagent/superagent&#39;)</code>。这样您的请求将显示在 Chrome 开发者工具的&quot;网络(Network)&quot;选项卡中。请注意，自动化测试套件未涵盖此环境，也未得到官方支持。</p>
<h2 id="使用代理发送请求">使用代理发送请求</h2>
<p>可以使用另一个作者的 <a href="https://www.npmjs.com/package/superagent-proxy">superagent-proxy</a> 模块</p>
<h2 id="翻译说明">翻译说明</h2>
<p>文档全部内容都是根据原英文文档翻译的，译者也没水平，所以如果有错误还请指出</p>
    </div>
    <a href="http://github.com/ladjs/superagent"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png" alt="Fork me on GitHub"></a>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.0/jquery.min.js"></script>
    <script>
      $('code').each(function(){
        $(this).html(highlight($(this).text()));
      });

      function highlight(js) {
        return js
          .replace(/</g, '&lt;')
          .replace(/>/g, '&gt;')
          .replace(/('.*?')/gm, '<span class="string">$1</span>')
          .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
          .replace(/(\d+)/gm, '<span class="number">$1</span>')
          .replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
          .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
      }
    </script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/3.0.0/tocbot.js"></script>
    <script>
      // Only use tocbot for main docs, not test docs
      if (document.querySelector('#superagent')) {
        tocbot.init({
          // Where to render the table of contents.
          tocSelector: '#menu',
          // Where to grab the headings to build the table of contents.
          contentSelector: '#content',
          // Which headings to grab inside of the contentSelector element.
          headingSelector: 'h2',
          smoothScroll: false
        });
      }
    </script>
  </body>
</html>
